/*
 * Copyright 2011 Helios Technologies.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.heliostech.gwt.widget.dialog.wizard;

import java.util.ArrayList;
import java.util.Collection;

import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.resources.client.ClientBundle;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.resources.client.CssResource.ImportedWithPrefix;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.ui.Button;
import com.heliostech.gwt.widget.dialog.base.BaseDialog;

/**
 * BaseDialog implementation to be used for wizard creation.
 * @author ldesdier
 */
public class BaseWizard extends BaseDialog {

	private final ArrayList<IWizardStep> steps = new ArrayList<IWizardStep>(0);
	
	protected interface Resources extends ClientBundle {
		@Source(Style.DEFAULT_CSS)
		@CssResource.NotStrict
	    Style baseWizardStyle();
	}
	
	@ImportedWithPrefix("helios-BaseWizard")
	public interface Style extends CssResource {
		String DEFAULT_CSS = "com/heliostech/gwt/widget/dialog/wizard/BaseWizard.css";
		
		String baseWizardWidget();

		String previousButton();

		String nextButton();

		String finishButton();

		String cancelButton();
		
	}
	
	private static Resources DEFAULT_RESOURCES;
	private static Resources getDefaultResources() {
		if (DEFAULT_RESOURCES == null) {
			DEFAULT_RESOURCES = GWT.create(Resources.class);
		}
		return DEFAULT_RESOURCES;
	}
	
	private final Style style;
	private final Button previous;
	private final Button next;
	private final Button finish;
	private final Button cancel;
	private IWizardStep current;
	private Command finishCommand;

	/**
	 * @param title
	 * @param showDescription
	 */
	public BaseWizard(String title) {
		this(title, true);
	}
	
	/**
	 * @param title
	 * @param showDescription
	 * @param modal
	 */
	public BaseWizard(String title, boolean modal) {
		this(title, modal, getDefaultResources());
	}
	
	/**
	 * @param title
	 * @param showDescription
	 * @param modal
	 * @param resources
	 */
	public BaseWizard(String title, boolean modal, BaseWizard.Resources resources) {
		super(title, true, modal);
		this.style = resources.baseWizardStyle();
		this.style.ensureInjected();
		addStyleName(style.baseWizardWidget());
		
		previous = new Button("Previous");
		previous.addStyleName(style.previousButton());
		previous.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				goPrevious();
			}
		});
		addButton(previous);
		
		next = new Button("Next");
		next.addStyleName(style.nextButton());
		next.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				goNext();
			}
		});
		addButton(next);
		
		finish = new Button("Finish");
		finish.addStyleName(style.finishButton());
		finish.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				doFinish();
			}
		});
		addButton(finish);
		
		cancel = new Button("Cancel");
		cancel.addStyleName(style.cancelButton());
		cancel.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				doCancel();
			}
		});
		addButton(cancel);
	}
	
	/* (non-Javadoc)
	 * @see com.google.gwt.user.client.ui.DialogBox#show()
	 * Ensure that the first step of the wizard is displayed when the wizard is shown.
	 */
	@Override
	public void show() {
		this.setCurrent(steps.get(0));
		super.show();
	}

	/**
	 * Set the wizard steps.
	 * @param steps A Collection of IWizardStep implementation.
	 */
	public void setSteps(Collection<IWizardStep> sts) {
		steps.addAll(sts);
	}

	/**
	 * Add a wizard step in the flow.
	 * @param s The step to be added.
	 */
	public void addStep(IWizardStep s) {
		steps.add(s);
	}
	
	/**
	 * Add a wizard step in the flow at the specified index.
	 * @param idx The index where to add the step.
	 * @param s The step to be added.
	 */
	public void addStep(int idx, IWizardStep s) {
		steps.add(idx, s);
	}

	/**
	 * Get the step at the specified index.
	 * @param idx The index of the step
	 * @return The IWizardStep implementation at the index.
	 */
	public IWizardStep getStep(int idx) {
		return steps.get(idx);
	}

	/**
	 * Remove the step at the specified index.
	 * @param idx The index of the step to be removed.
	 */
	public void remove(int idx) {
		steps.remove(idx);
	}

	/**
	 * Go to the previous step if allowed.
	 */
	private void goPrevious() {
		int cIdx = getCurrent().getIndex();
		int pIdx = cIdx-1;
		if(pIdx>-1) {
			IWizardStep p = getStep(pIdx);
			if(getCurrent().onBeforePrevious()) {
				setCurrent(p);
			}
		}
	}

	/**
	 * Go to the next step if allowed.
	 */
	private void goNext() {
		int cIdx = getCurrent().getIndex();
		int nIdx = cIdx+1;
		if(nIdx < steps.size()) {
			IWizardStep n = getStep(nIdx);
			if(getCurrent().onBeforeNext()) {
				setCurrent(n);
			}
		}
	}
	
	/**
	 * Execute the finish process when the user click on the Finish button.
	 */
	private void doFinish() {
		IWizardStep c = getCurrent();
		if(c.onBeforeFinish()) {
			finishCommand.execute();
		}
	}
	
	/**
	 * Execute the cancel process. Basically reset the wizard to its initial state and dismiss it.
	 */
	private void doCancel() {
		for(IWizardStep s : steps) {
			s.cleanup();
		}
		setCurrent(steps.get(0));
		dismiss();
	}

	/**
	 * Return the step that is currently displayed.
	 * @return the current
	 */
	public IWizardStep getCurrent() {
		return current;
	}

	/**
	 * Set the displayed step in the wizard.
	 * @param current the current to set
	 */
	public void setCurrent(IWizardStep ws) {
		if(ws.getStepIcon() != null) {
			setDialogDescription(ws.getStepAction(), ws.getStepDescription(), ws.getStepIcon());
		} else {
			setDialogDescription(ws.getStepAction(), ws.getStepDescription());
		}
		setContent(ws.getStepContent());
		setNextEnabled(ws.allowNext());
		setPreviousEnabled(ws.allowPrevious());
		setFinishEnabled(ws.allowFinish());
		this.current = ws;
	}
	
	/**
	 * Enable/disable the Next button.
	 * @param enabled boolean that indicates if the button should be enabled.
	 */
	public void setNextEnabled(boolean enabled) {
		next.setEnabled(enabled);
	}
	
	/**
	 * Enable/disable the Previous button.
	 * @param enabled boolean that indicates if the button should be enabled.
	 */
	public void setPreviousEnabled(boolean enabled) {
		previous.setEnabled(enabled);
	}
	
	/**
	 * Enable/disable the Finish button.
	 * @param enabled boolean that indicates if the button should be enabled.
	 */
	public void setFinishEnabled(boolean enabled) {
		finish.setEnabled(enabled);
	}
	
	/**
	 * Set the command to be executed when user clicks on the Finish button.
	 * @param finishCommand the finishCommand to set
	 */
	public void setFinishCommand(Command finishCommand) {
		this.finishCommand = finishCommand;
	}
	
}
